use regex::Regex;
// use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ValidationError {
    #[error("Field '{field}' is required")]
    Required { field: String },
    
    #[error("Field '{field}' must be between {min} and {max} characters")]
    Length { field: String, min: usize, max: usize },
    
    #[error("Field '{field}' has invalid format")]
    Format { field: String },
    
    #[error("Field '{field}' contains invalid characters")]
    InvalidCharacters { field: String },
    
    #[error("Field '{field}' exceeds maximum value {max}")]
    MaxValue { field: String, max: i64 },
    
    #[error("Field '{field}' is below minimum value {min}")]
    MinValue { field: String, min: i64 },
}

pub type ValidationResult<T> = Result<T, ValidationError>;

#[derive(Debug, Clone)]
pub struct ValidationRule {
    pub required: bool,
    pub min_length: Option<usize>,
    pub max_length: Option<usize>,
    pub pattern: Option<Regex>,
    pub min_value: Option<i64>,
    pub max_value: Option<i64>,
}

impl Default for ValidationRule {
    fn default() -> Self {
        Self {
            required: false,
            min_length: None,
            max_length: None,
            pattern: None,
            min_value: None,
            max_value: None,
        }
    }
}

impl ValidationRule {
    pub fn required() -> Self {
        Self {
            required: true,
            ..Default::default()
        }
    }
    
    pub fn length(min: usize, max: usize) -> Self {
        Self {
            min_length: Some(min),
            max_length: Some(max),
            ..Default::default()
        }
    }
    
    pub fn pattern(regex: &str) -> Result<Self, regex::Error> {
        Ok(Self {
            pattern: Some(Regex::new(regex)?),
            ..Default::default()
        })
    }
    
    pub fn range(min: i64, max: i64) -> Self {
        Self {
            min_value: Some(min),
            max_value: Some(max),
            ..Default::default()
        }
    }
}

pub struct Validator {
    rules: HashMap<String, ValidationRule>,
}

impl Validator {
    pub fn new() -> Self {
        Self {
            rules: HashMap::new(),
        }
    }
    
    pub fn add_rule(mut self, field: &str, rule: ValidationRule) -> Self {
        self.rules.insert(field.to_string(), rule);
        self
    }
    
    pub fn validate_string(&self, field: &str, value: Option<&str>) -> ValidationResult<()> {
        let rule = self.rules.get(field);
        
        if let Some(rule) = rule {
            // Check required
            if rule.required && (value.is_none() || value.unwrap().is_empty()) {
                return Err(ValidationError::Required {
                    field: field.to_string(),
                });
            }
            
            if let Some(value) = value {
                // Check length
                if let (Some(min), Some(max)) = (rule.min_length, rule.max_length) {
                    let len = value.len();
                    if len < min || len > max {
                        return Err(ValidationError::Length {
                            field: field.to_string(),
                            min,
                            max,
                        });
                    }
                }
                
                // Check pattern
                if let Some(pattern) = &rule.pattern {
                    if !pattern.is_match(value) {
                        return Err(ValidationError::Format {
                            field: field.to_string(),
                        });
                    }
                }
                
                // Check for dangerous characters
                if value.contains('<') || value.contains('>') || value.contains('&') {
                    return Err(ValidationError::InvalidCharacters {
                        field: field.to_string(),
                    });
                }
            }
        }
        
        Ok(())
    }
    
    pub fn validate_number(&self, field: &str, value: Option<i64>) -> ValidationResult<()> {
        let rule = self.rules.get(field);
        
        if let Some(rule) = rule {
            // Check required
            if rule.required && value.is_none() {
                return Err(ValidationError::Required {
                    field: field.to_string(),
                });
            }
            
            if let Some(value) = value {
                // Check range
                if let Some(min) = rule.min_value {
                    if value < min {
                        return Err(ValidationError::MinValue {
                            field: field.to_string(),
                            min,
                        });
                    }
                }
                
                if let Some(max) = rule.max_value {
                    if value > max {
                        return Err(ValidationError::MaxValue {
                            field: field.to_string(),
                            max,
                        });
                    }
                }
            }
        }
        
        Ok(())
    }
}

// Common validation patterns
pub struct ValidationPatterns;

impl ValidationPatterns {
    pub fn email() -> Result<Regex, regex::Error> {
        Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
    }
    
    pub fn username() -> Result<Regex, regex::Error> {
        Regex::new(r"^[a-zA-Z0-9_-]{3,20}$")
    }
    
    pub fn filename() -> Result<Regex, regex::Error> {
        Regex::new(r"^[a-zA-Z0-9._-]+$")
    }
    
    pub fn path() -> Result<Regex, regex::Error> {
        Regex::new(r"^[a-zA-Z0-9._/-]+$")
    }
}

// Sanitization functions
pub fn sanitize_string(input: &str) -> String {
    input
        .replace('<', "&lt;")
        .replace('>', "&gt;")
        .replace('&', "&amp;")
        .replace('"', "&quot;")
        .replace('\'', "&#x27;")
}

pub fn sanitize_filename(input: &str) -> String {
    input
        .chars()
        .filter(|c| c.is_alphanumeric() || *c == '.' || *c == '_' || *c == '-')
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_required_validation() {
        let validator = Validator::new()
            .add_rule("name", ValidationRule::required());
        
        assert!(validator.validate_string("name", None).is_err());
        assert!(validator.validate_string("name", Some("")).is_err());
        assert!(validator.validate_string("name", Some("John")).is_ok());
    }
    
    #[test]
    fn test_length_validation() {
        let validator = Validator::new()
            .add_rule("name", ValidationRule::length(3, 10));
        
        assert!(validator.validate_string("name", Some("Jo")).is_err());
        assert!(validator.validate_string("name", Some("John")).is_ok());
        assert!(validator.validate_string("name", Some("VeryLongName")).is_err());
    }
    
    #[test]
    fn test_pattern_validation() {
        let validator = Validator::new()
            .add_rule("email", ValidationRule::pattern(r"^[^@]+@[^@]+\.[^@]+$").unwrap());
        
        assert!(validator.validate_string("email", Some("invalid")).is_err());
        assert!(validator.validate_string("email", Some("test@example.com")).is_ok());
    }
    
    #[test]
    fn test_sanitization() {
        assert_eq!(sanitize_string("<script>alert('xss')</script>"), 
                   "&lt;script&gt;alert(&#x27;xss&#x27;)&lt;/script&gt;");
        
        assert_eq!(sanitize_filename("file<>name.txt"), "filename.txt");
    }
}
